package org.ytor.core.sqlflow.dsl.template;

import lombok.extern.slf4j.Slf4j;
import org.ytor.core.sqlflow.dsl.eval.EvalContext;
import org.ytor.core.sqlflow.dsl.eval.ExprEvaluator;
import org.ytor.core.sqlflow.dsl.eval.MapEvalContext;
import org.ytor.core.sqlflow.dsl.expr.ExprAstParser;
import org.ytor.core.sqlflow.dsl.expr.node.ExprNode;
import org.ytor.core.sqlflow.dsl.template.node.TemplateNode;
import org.ytor.core.sqlflow.dsl.template.node.support.BlockNode;
import org.ytor.core.sqlflow.dsl.template.node.support.ForNode;
import org.ytor.core.sqlflow.dsl.template.node.support.IfNode;
import org.ytor.core.sqlflow.dsl.template.node.support.TextNode;
import org.ytor.core.sqlflow.dsl.token.Tokenizer;
import org.ytor.core.sqlflow.enums.SqlType;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * created by yangtong on 2025/8/7 12:10:07
 * <br/>
 * 模板渲染器
 */
@Slf4j
public class TemplateRenderer {

    private final ExprEvaluator evaluator = new ExprEvaluator();

    public RenderResult render(TemplateNode node, EvalContext ctx) {
        StringBuilder sb = new StringBuilder();
        List<Object> params = new ArrayList<>();
        renderNode(node, ctx, sb, params);
        String sql = sb.toString();
        return new RenderResult(sql, params, SqlType.parseType(sql));
    }

    private void renderNode(TemplateNode node, EvalContext ctx, StringBuilder sb, List<Object> params) {
        if (node instanceof TextNode t) {
            sb.append(renderText(t.text, ctx, params)).append("\n");
        } else if (node instanceof BlockNode b) {
            for (TemplateNode child : b.children) {
                renderNode(child, ctx, sb, params);
            }
        } else if (node instanceof IfNode i) {
            Object cond = evaluator.evaluate(i.condition, ctx);
            if (toBoolean(cond)) {
                renderNode(i.body, ctx, sb, params);
            }
        } else if (node instanceof ForNode f) {
            Object iterable = ctx.getVariable(f.listVar);
            List<?> list = resolveToList(iterable);

            for (int i = 0; i < list.size(); i++) {
                Object item = list.get(i);

                // 构建FOR循环体上下文，内置 index/item 变量
                Map<String, Object> scope = new HashMap<>();
                scope.put("index", i);
                scope.put("item", item);
                EvalContext loopCtx = new MergedEvalContext(ctx, new MapEvalContext().putAll(scope));

                renderNode(f.body, loopCtx, sb, params);
            }
        }
    }

    private boolean toBoolean(Object o) {
        if (o == null) return false;
        if (o instanceof Boolean b) return b;
        if (o instanceof Number n) return n.doubleValue() != 0;
        if (o instanceof String s) return !s.isEmpty();
        return true;
    }

    private List<?> resolveToList(Object val) {
        if (val instanceof List<?>) return (List<?>) val;
        if (val instanceof String str) return Arrays.asList(str.split(","));
        throw new IllegalArgumentException("for: 后面必须是数组或字符串");
    }

    private String renderText(String text, EvalContext ctx, List<Object> params) {
        return replacePlaceholders(text, ctx, params);
    }


    private String replacePlaceholders(String text, EvalContext ctx, List<Object> params) {
        Pattern pattern = Pattern.compile("([$#])\\{(.*?)}");
        Matcher matcher = pattern.matcher(text);
        StringBuffer sb = new StringBuffer();

        while (matcher.find()) {
            String prefix = matcher.group(1); // $ or #
            String exprText = matcher.group(2).trim();
            String replacement;

            try {
                // 拆分表达式 + 默认值
                String[] parts = parseExprAndDefault(exprText);
                String mainExpr = parts[0];
                String defaultExpr = parts[1];

                ExprNode exprNode = new ExprAstParser(new Tokenizer(mainExpr).tokenize()).parseExpression();
                Object val = evaluator.evaluate(exprNode, ctx);

                // 处理默认值
                if (val == null && defaultExpr != null) {
                    ExprNode defExpr = new ExprAstParser(new Tokenizer(defaultExpr).tokenize()).parseExpression();
                    val = evaluator.evaluate(defExpr, ctx);
                }

                if (prefix.equals("$")) {
                    // 直接插值
                    replacement = valToString(val);
                } else {
                    // JDBC 参数 ? 占位
                    replacement = valToString(val, params);
                }
            } catch (Exception e) {
                replacement = "[ERROR:" + exprText + "]";
            }

            matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement));
        }

        matcher.appendTail(sb);
        return sb.toString();
    }


    private String[] parseExprAndDefault(String exprText) {
        int depth = 0;
        for (int i = 0; i < exprText.length(); i++) {
            char c = exprText.charAt(i);
            if (c == '(') depth++;
            else if (c == ')') depth--;
            else if (c == ',' && depth == 0) {
                // 找到最外层逗号：表达式 + 默认值
                String expr = exprText.substring(0, i).trim();
                String defaultVal = exprText.substring(i + 1).trim();
                return new String[]{expr, defaultVal};
            }
        }
        return new String[]{exprText.trim(), null};
    }

    private String valToString(Object val) {
        if (val == null) return "";
        if (val instanceof List<?>) {
            return ((List<?>) val).stream()
                    .map(this::valToString)
                    .collect(Collectors.joining(","));
        }
        return val.toString();
    }

    private String valToString(Object val, List<Object> params) {
        String replacement;
        // JDBC 参数 ? 占位
        if (val instanceof List<?> list && !list.isEmpty()) {
            // 集合类型：生成多个?占位符
            replacement = list.stream()
                    .map(item -> "?")
                    .collect(Collectors.joining(", "));
            params.addAll(list);
        } else if (val != null && val.getClass().isArray()) {
            // 数组类型：转换为List处理
            List<?> arrayList = Arrays.asList((Object[]) val);
            replacement = arrayList.stream()
                    .map(item -> "?")
                    .collect(Collectors.joining(", "));
            params.addAll(arrayList);
        } else {
            // 单个值
            replacement = "?";
            params.add(val);
        }
        return replacement;
    }

}
